﻿namespace CacheProvider.AppFabric
{
    #region N A M E S P A C E   I M P O R T S

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using CacheProviderInterfaces;
    using Microsoft.ApplicationServer.Caching;

    #endregion

    public class DataCacheFactoryPool : IDataCacheFactoryPool
    {
        #region P R I V A T E   A T T R I B U T E S

        private readonly Int32 _maxPoolSize;
        private readonly Queue<DataCacheFactoryEntry> _dataCacheFactoriesQueue;

        private static readonly object Serializer = new object();

        #endregion

        #region C O N S T R U C T O R ( S )

        /// <summary>
        /// Constructor to create a pool of DataCacheFactories
        /// </summary>
        /// <param name="maxPoolSize">Maximum number of elements in the pool</param>
        /// <param name="eagerLoad">Whether to initialize all the DataCacheFactory elements during construction</param>
        public DataCacheFactoryPool(Int32 maxPoolSize, bool eagerLoad)
        {
            this._maxPoolSize = maxPoolSize;

            this._dataCacheFactoriesQueue = new Queue<DataCacheFactoryEntry>(maxPoolSize);

            if (!eagerLoad) return;

            for (var i = 0; i < this._maxPoolSize; i++)
            {
                var dataCacheFactory = new DataCacheFactory();
                var factoryEntry = new DataCacheFactoryEntry(dataCacheFactory);
                this._dataCacheFactoriesQueue.Enqueue(factoryEntry);
            }
        }

        #endregion

        #region P U B L I C    M E T H O D S

        /// <summary>
        /// Provides a DataCacheFactory From A Pool Of Factories
        /// </summary>
        /// <returns>An Instance of <see cref="DataCacheFactory"/></returns>
        public DataCacheFactory GetDataCacheFactory()
        {
            lock (Serializer)
            {
                var dataCacheFactory = this.CreateOrExtractDataCacheFactory();
                return dataCacheFactory;
            }
        }

        public void ClearPool()
        {
            lock (Serializer)
            {
                while (this._dataCacheFactoriesQueue.Count > 0)
                {
                    var dataCacheFactoryEntry = this._dataCacheFactoriesQueue.Dequeue();
                    if (dataCacheFactoryEntry.DataCacheFactory != null)
                        dataCacheFactoryEntry.DataCacheFactory.Dispose();
                }
            }
        }

        #endregion


        #region P R I V A T E   M E T H O D S

        private DataCacheFactory CreateOrExtractDataCacheFactory()
        {
            DataCacheFactory dataCacheFactory;
            //If the number of factories is still lesser than the pool size,
            //create a new one, enqueue and return the same.
            if (this._dataCacheFactoriesQueue.Count() < this._maxPoolSize)
            {
                dataCacheFactory = new DataCacheFactory();
                var factoryEntry = new DataCacheFactoryEntry(dataCacheFactory);
                this._dataCacheFactoriesQueue.Enqueue(factoryEntry);
            }
            else
            {
                var nextCacheFactoryEntry = this._dataCacheFactoriesQueue.Dequeue();
                dataCacheFactory = nextCacheFactoryEntry.DataCacheFactory;
                //Enqueueing again at the end to make a circular queue
                this._dataCacheFactoriesQueue.Enqueue(nextCacheFactoryEntry);
            }

            return dataCacheFactory;
        }

        #endregion

        #region IDataCacheFactoryPool Members

        public int PoolSize
        {
            get { return this._dataCacheFactoriesQueue.Count; }
        }

        #endregion
    }
}